#include "gpio.h"
#include "interrupt.h"
#include "DrvUART010.h"
//#include "uwifi_platform.h"
//#include "uwifi_sdio.h"
//#include "uwifi_msg.h"
#include "unione_lite.h"
#include <string.h>

#define GPIO_PRINT(format, ...)	 			fLib_printf("%s %d:"format, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define set_bit(x,b) (x) |= (1U<<(b))
#define clear_bit(x,b) (x) &= ~(1U<<(b))

gpio_callback_t gpio_callback[TOTAL_GPIO_NUM] = {{0}};

void uni_gpio_irq_handler(void *arg)
{
	int value = 0;
	unsigned int status, gpioNum;
	status = read_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_MASKED);
	write_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_CLEAR,status);

	for(gpioNum = 0; gpioNum < TOTAL_GPIO_NUM; gpioNum++){
		if(((status >> gpioNum) & 0x1)){
			if(gpio_callback[gpioNum].func){
				uni_get_gpio_value(gpioNum, &value);
				gpio_callback[gpioNum].func(gpioNum, value);
			}else{
				fLib_printf("uni_gpio_irq_handler failed! gpio_callback[%d].func = 0x%08x is NULL!\n", gpioNum, gpio_callback[gpioNum].func);
			}
		}
	}

    return;
}

/***************************************************************
gpio:
   gpio num(0-31)
dir:
   1:out
   0:in
****************************************************************/
void uni_set_gpio_dir(int gpio,int dir)
{
     unsigned int reg;
	 reg = read_mreg32(GPIO_BASE + FTGPIO010_OFFSET_PINDIR);
     if(dir){
          reg |= (1<<gpio);
		  write_mreg32(GPIO_BASE + FTGPIO010_OFFSET_PINDIR,reg);
     }else{
          reg &= (~(1<<gpio));
	      write_mreg32(GPIO_BASE + FTGPIO010_OFFSET_PINDIR,reg);
     }
}

/***************************************************************
gpio:
   gpio num(0-31)
value:
   1:high
   0:low
****************************************************************/

void uni_set_gpio_value(int gpio,int value)
{
	unsigned int reg;
	reg = read_mreg32(GPIO_BASE + FTGPIO010_OFFSET_DATAOUT);
	if(value){
		 reg |= (1<<gpio);
		 //GPIO_PRINT("reg = 0x%x\r\n", reg);

		 write_mreg32(GPIO_BASE + FTGPIO010_OFFSET_DATAOUT,reg);
	}else{
		 reg &= (~(1<<gpio));
		 //GPIO_PRINT("reg = 0x%x\r\n", reg);
		 write_mreg32(GPIO_BASE + FTGPIO010_OFFSET_DATAOUT,reg);
	}
}

/***************************************************************
gpio:
   gpio num(0-31)
value:
   current pin status
****************************************************************/
void uni_get_gpio_value(int gpio,int *value)
{
	unsigned int regval;
	regval = read_mreg32(GPIO_BASE + FTGPIO010_OFFSET_DATAIN);
	*value = (regval >> gpio) & 0x1;
}

/***************************************************************
gpio:
   gpionum(0-31)
pull_mode:
   0:pull_down, 1:pull_up, 2:not_pull
****************************************************************/
void uni_set_gpio_pull_mode(int gpio,int pull_mode)
{
  unsigned int reg;
  reg = read_mreg32(GPIO_BASE + FTGPIO010_OFFSET_PULLEN);
  switch (pull_mode){
    case 0:
      write_mreg32(GPIO_BASE + FTGPIO010_OFFSET_DATAOUT, set_bit(reg, gpio));
      reg = read_mreg32(GPIO_BASE + FTGPIO010_OFFSET_PULLTYPE);
      write_mreg32(GPIO_BASE + FTGPIO010_OFFSET_PULLTYPE, clear_bit(reg, gpio));
      break;
    case 1:
      write_mreg32(GPIO_BASE + FTGPIO010_OFFSET_DATAOUT, set_bit(reg, gpio));
      reg = read_mreg32(GPIO_BASE + FTGPIO010_OFFSET_PULLTYPE);
      write_mreg32(GPIO_BASE + FTGPIO010_OFFSET_PULLTYPE, set_bit(reg, gpio));
      break;
    case 2:
      write_mreg32(GPIO_BASE + FTGPIO010_OFFSET_DATAOUT, clear_bit(reg, gpio));
      break;
    default:
      fLib_printf("error: set gpio pull mode fail, unknown mode %d \n", pull_mode);
      return;
  }
}


/***************************************************************
gpio:
   gpionum(0-31)
type:
   enum {
     GPIO_INT_NEG_EDGE = 0,     ///< 下降沿触发
     GPIO_INT_POS_EDGE,     ///< 上升沿触发
     GPIO_INT_BOTH_EDGE,    ///< 双沿触发
     GPIO_INT_HIGH_LEVEL,   ///< 高电平触发
     GPIO_INT_LOW_LEVEL,    ///< 低电平触发
   }
****************************************************************/
int uni_gpio_isr_cfg(int gpio_num, int type)
{
  unsigned int num;
  GPIO_PRINT("gpio_num = %d, type = %d \r\n", gpio_num, type);

  switch(type){
    case 0:
    case 1:
    case 2:
      num = read_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_TRIG);
      write_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_TRIG, clear_bit(num, gpio_num));
      break;
    case 3:
    case 4:
      num = read_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_TRIG);
      write_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_TRIG, set_bit(num, gpio_num));
      break;
    default:
      break;
  }
  switch(type){
    case 0:
      num = read_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_BOTH);
      write_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_BOTH, clear_bit(num, gpio_num));
      num = read_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_RISENEG);
      write_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_RISENEG, set_bit(num, gpio_num));
      break;
    case 1:
      num = read_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_BOTH);
      write_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_BOTH, clear_bit(num, gpio_num));
      num = read_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_RISENEG);
      write_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_RISENEG, clear_bit(num, gpio_num));
      break;
    case 2:
      num = read_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_BOTH);
      write_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_BOTH, set_bit(num, gpio_num));
      break;
    case 3:
      num = read_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_RISENEG);
      write_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_RISENEG, clear_bit(num, gpio_num));
      break;
    case 4:
      num = read_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_RISENEG);
      write_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_RISENEG, set_bit(num, gpio_num));
      break;
    default:
      break;
  }

  return 0;
}

int uni_gpio_isr_enable(bool enable, int gpio_num)
{
  unsigned int num;

  if(enable){
    num = read_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_MASK);
    write_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_MASK, clear_bit(num, gpio_num));
    num = read_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_EN);
    write_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_EN, set_bit(num, gpio_num));
  } else {
    num = read_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_MASK);
    write_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_MASK, set_bit(num, gpio_num));
    num = read_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_EN);
    write_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_EN, clear_bit(num, gpio_num));
  }

  return 0;
}


void uni_gpio_irq_init(void)
{
  /* sanitize */
  write_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_CLEAR,~0);

  /* unmask all */
  //write_mreg32(GPIO_BASE + FTGPIO010_OFFSET_INTR_MASK,0);

  if(fLib_ConnectInt(FTGPIO_IRQ, (PrVoid)uni_gpio_irq_handler) == false){
    GPIO_PRINT("Fail to register gpio_interrupt \r\n");
    return;
  }
}

void uni_gpio_irq_enable(void)
{
  fLib_EnableInt(FTGPIO_IRQ);
}

void uni_gpio_irq_disable(void)
{
  fLib_DisableInt(FTGPIO_IRQ);
}


